<?php

namespace App\Model;


use EasySwoole\FastCache\Cache;
use EasySwoole\ORM\Exception\Exception;
use Throwable;

/**
 * RuleModel
 * Class RuleModel
 * Create With ClassGeneration
 * @property int $id // 权限id
 * @property string $pid // 父id
 * @property int $is_menu // (0|1)是否为菜单
 * @property string $rule // 路径
 * @property string $name // 名称
 * @property string $icon // 图标
 * @property int $weight // 权重
 * @property int $status // (0|1)启用状态
 * @property string $method // 可操作标识:"GET","PATCH","DELETE","POST"
 * @property string $remark // 备注
 */
    class RuleModel extends BaseModel
    {
        const MENUSKEY = "admin-menus";

        protected $tableName = 'rule';

        public function getList(int $page = 1, int $pageSize = 10, string $field = '*'): array
        {
            $list = $this
                ->withTotalCount()
                ->order($this->schemaInfo()->getPkFiledName(), 'DESC')
                ->field($field)
                ->page($page, $pageSize)
                ->all();
            $total = $this->lastQueryResult()->getTotalCount();
            $data = [
                'page'=>$page,
                'pageSize'=>$pageSize,
                'list'=>$list,
                'total'=>$total,
                'pageCount'=>ceil($total / $pageSize)
            ];
            return $data;
        }


        public function addData(
            string $pid,
            int $is_menu,
            string $rule,
            string $name,
            string $icon,
            int $weight,
            int $status,
            string $method,
            string $remark
        ): self {
            $data = [
                'pid'=>$pid,
                'is_menu'=>$is_menu,
                'rule'=>$rule,
                'name'=>$name,
                'icon'=>$icon,
                'weight'=>$weight,
                'status'=>$status,
                'method'=>$method,
                'remark'=>$remark,
            ];
            $model = new self($data);
            $model->save();
            return $model;
        }

        /** 获取权限菜单(走穿透)
         * @return array|mixed
         * @throws Exception
         * @throws Throwable
         * *    author 韩天尊
         * 获取某个菜单 = menus("id", 1);
         */
        public function menus(string $key="", $value=""):array
        {
            $menus = json_decode(Cache::getInstance()->get(self::MENUSKEY), 1);
            if (empty($menus)){
                $menus = dbObjectToArray($this->all());
                if (!empty($menus)){
                    Cache::getInstance()->set(self::MENUSKEY, json_encode($menus), 300);
                }
            }
            if (!empty($menus) && !empty($key) && !empty($value)){
                foreach ($menus as $menu){
                    if ($menu[$key] == $value){
                        $menus = $menu; break; }
                }
            }
            return $menus;
        }

        /** 删除menus菜单缓存
         * *    author 韩天尊
         */
        public function unsetCacheMenusKey()
        {
            if (Cache::getInstance()->get(self::MENUSKEY)){
                Cache::getInstance()->unset(self::MENUSKEY);
            }
        }

        /** 获取菜单带排序
         * @return array
         * @throws Exception
         * @throws Throwable
         * *    author 韩天尊
         */
        public function getRules($id=0)
        {
            $result = [];
            $menus = $this->menus();
            $this->traverseMenu($menus, $result, $id);
            $result = $this->rule_sort($result, 1);
            return $result;
        }

        /** 菜单冒泡排序
         * @param array $array
         * @param int $sortType
         * @return array
         * *    author 韩天尊
         */
        function rule_sort(array $array, int $sortType=0): array
        {
            $arrayLength = count($array)-1;
            for ($i=0; $i<$arrayLength; $i++){
                for($j=0; $j<$arrayLength-$i; $j++){
                    if (isset($array[$j])&&isset($array[$j+1])){
                        if (($sortType===0)? ($array[$j]['weight'] > $array[$j+1]['weight']):($array[$j]['weight'] < $array[$j+1]['weight'])){
                            $container = $array[$j];
                            $array[$j] = $array[$j+1];
                            $array[$j+1] = $container;
                        }
                    }
                }
            }
            return $array;
        }

        /** 无限级菜单合并带排序
         * @param array $menus
         * @param array $result
         * @param int $pid
         * *    author 韩天尊
         */
        function traverseMenu(array $menus, array &$result, $pid = 0)
        {
            foreach ($menus as $child_menu) {
                // 子菜单的 pid = $pid
                // 说明它是 $pid 下面的子菜单
                if ($child_menu['pid'] == $pid) {
                    if ($child_menu['status'] !== 1){//条件过滤
                        continue;
                    }
                    // 包装为成我们需要的格式
                    $item = [
                        'id' => $child_menu['id'],
                        'name' => $child_menu['name'],
                        'pid' => $child_menu['pid'],
                        'is_menu' => $child_menu['is_menu'],
                        'icon' => $child_menu['icon'],
                        'weight' => $child_menu['weight'],
                        'status' => $child_menu['status'],
                        'method' => $child_menu['method'],
                        'children' => []
                    ];
                    // 递归上面的操作，装完 所有 属于 $pid 下面的子菜单
                    $this->traverseMenu($menus, $item['children'], $child_menu['id']);
                    //排序
                    if (!empty($item['children']) && (count($item['children'])>=2)){
                        $item['children'] = $this->rule_sort($item['children'], 1);
                    }
                    // 至此完成 $pid 级下的所有子菜单装入 result 中
                    $result[] = $item;
                }
            }
        }

        /** 权限节点状态开关
         * @param $id
         * @param $status
         * @throws \EasySwoole\Mysqli\Exception\Exception
         * @throws Exception
         * @throws Throwable
         * *    author 韩天尊
         */
        public function switchStatus($id, $status)
        {
            $statusObject = $this->get(["id"=>$id]);
            if ($statusObject->status == $status){
                return;
            }
            if ($status == 0){//关闭 找到此节点下所有节点并关闭
                $childrenIds = $this->relation($statusObject->id, "pid","id", 999);
                foreach ($childrenIds as $children_id){
                    $this->where(["id"=>$children_id])->update(["status"=>$status]);
                }
            }
            if ($status == 0){//打开 找到此节点之上的所有父节点并打开
                $parentIds = $this->relation($statusObject->id, "id","pid", 999);
                foreach ($parentIds as $parent_id){
                    if ($parent_id == 0){continue;}
                    $this->where(["id"=>$parent_id])->update(["status"=>$status]);
                }
            }
            $statusObject->status = $status;
            $statusObject->update();
            $this->unsetCacheMenusKey();//删除缓存
        }


        /** 关系获取
         * @param $id //找关于谁
         * @param string $whereFiled
         * @param string $columnFiled
         * @param int $hierarchy //找多少层
         * @return array
         * *    author 韩天尊
         *
         * 示例：
         *  relation(6,"id","pid", 999999);找6的所有上级 1对1
         *  relation(1,"pid","id", 999999);找1的所有下级 1对多
         */
        public function relation($id, string $whereFiled, string $columnFiled, int $hierarchy): array
        {
            $ids = [$id];
            $returnArray = [];
            for ($i=1; $i<=$hierarchy; $i++){
                $nextArray = $this->findRelation($ids,$whereFiled,$columnFiled);
                if(!empty($nextArray)){
                    $returnArray = array_merge($returnArray, $nextArray);
                }else{break;}
                $ids = $nextArray;
            }
            return $returnArray;
        }
        function findRelation(array $ids, string $whereFiled, string $columnFiled): array
        {
            $relation_ids = [];
            foreach ($ids as $id){
                $dbIds = $this->where([$whereFiled=>$id])->column($columnFiled);
                if (!empty($dbIds)){
                    $relation_ids = array_merge($relation_ids, $dbIds);
                }
            }
            return $relation_ids;
        }

    }

